Android窗口管理分析(三):WMS窗口的组织形式
本文总共包括以下几点:
- 窗口的分组管理:应用窗口组、子窗口组、系统窗口组
- Activity、Dialg应用窗口及PopWindow子窗口的添加原理跟注意事项
- 窗口的Z次序管理:窗口的分配序号、次序调整等
- WMS中窗口次序分配如何影响SurfaceFlinger服务
各种窗口type和窗口类型的对应关系是什么样的?
窗口和对应类型
窗口type值 | 窗口类型 |
---|---|
FIRST_APPLICATION_WINDOW=1 | 开始应用程序窗口 |
TYPE_BASE_APPLICATION=1 | 所有程序窗口的base窗口,其他应用程序都显示在它上面 |
TYPE_APPLICATION=2 | 普通应用程序窗口,token必须设置为Activity的token |
TYPE_APPLICATION_STARTING=3 | 应用程序启动时所显示的窗口 |
LAST_APPLICATION_WINDOW=99 | 结束应用程序窗口 |
一般Activity都是TYPE_BASE_APPLICATION类型的,而TYPE_APPLICATION主要是用于Dialog,再看下子窗口类型
子窗口和对应类型
窗口type值 | 窗口类型 |
---|---|
FIRST_SUB_WINDOW=1000 | SubWindows子窗口,子窗口的Z序和坐标空间 |
TYPE_APPLICATION_PANEL =1000 | 面板窗口,显示于宿主窗口的上层 |
TYPE_APPLICATION_MEDIA=1001 | 媒体窗口(例如视频),显示于宿主窗口下层 |
TYPE_APPLICATION_SUB_PANEL=1002 | 应用程序窗口的子面板,显示于所有面板窗口的上层 |
TYPE_APPLICATION_ATTACHED_DIALOG=1003 | 对话框,类似于面板窗口,显示于所有面板窗口的上层 |
TYPE_APPLICATION_MEDIA_OVERLAY=1004 | 媒体信息,显示在媒体层和程序窗口之间,需要实现半透明效果 |
LAST_SUB_WINDOW=1999 | 结束子窗口 |
系统窗口类型
窗口type值 | 窗口类型 |
---|---|
FIRST_SYSTEM_WINDOW=2000 | 系统窗口 |
TYPE_STATUS_BAR=FIRST_SYSTEM_WINDOW | 状态栏 |
TYPE_SYSTEM_ALERT=FIRST_SYSTEM_WINDOW+3 | 系统提示,出现在应用程序窗口之上 |
TYPE_TOAST=FIRST_SYSTEM_WINDOW+5 | 显示Toast |
窗口的分组原理是什么样的?
Android的窗口是以token来分组的
windowToken包含一个WindowList,包含一系列的WindowState
Activity对应token和WindowToken的添加过程是什么样的?
- AMS将Activity的token加入WMS中,为Activity创建APPWindowToken。
- Activity分组在Activity显示之前就被AMS添加到WMS中,之后AMS才会去通知App新建Activity,并将Activity的Window添加到WMS中。
- 启动Activity步骤:新建一个Activity,并为Activity创建一个appContext,这个Context主要是为了activity.attach使用的,其实是单纯new一个ContextImpl,之后Activity会利用attach函数将ContextImpl绑定在自己身上。
创建ActivityContext
- 为Activity绑定ContextImpl,因为Activity只是一个ContextWrapper。
- new一个PhoneWindow并设置回调。
- 利用当前的WindowManagerImpl为Window创建一个WindowManagerImpl并设置它的parentWindow。
setWindowManager
- 将window的WindowManager传递给Activity,作为Activity的WindowManager
- Activity通过getSystemService获取WindowManager服务时,直接返回了Window的WindowManagerImpl
getSystemService
- 通过需要获得的service名字返回不同manager对象。
handleResumeActivity
- 获取activity的window对象,如果用户没有通过setContentView方式新建DecorView,这里会利用PhoneWindow的getDecorView()新建DecorView,并把decorview设置为不可见。
- 获取windowManager以及window的各属性。添加decorview到WMS管理。
WindowManagerImpl的addView
- 最终会调用WindowManagerGlobal的addView方法
- 调整wparams的token参数
- 新建ViewRootImpl,并利用wparams参数添加窗口
- ViewRootImpl设置view
adjustLayoutParamsForSubWindow
- 对于Activity来说,wp.token = mContainer其实是AMS端传过来的IApplicationToken
- 在ViewRootImpl中setView的时候,利用IWindowSession代理与WMS端的Session通信,将窗口及token信息传递到WMS端,其中IApplicationToken就是该Activity处于的分组
- 在WMS端,根据IApplicationToken IBinder键值,从全局的mTokenMap。
Dialog必须用Activity的context?
在添加到WMS时,Dialog窗口属性是WindowManager.LayoutParams.TYPE_APPLICATION,同样属于应用窗口,所以必须使用Activity的AppToken才可以。
Dialog和Activity共享一个同一个WindowManager(也就是WindowManagerImpl),而WindowManagerImpl里面有个Window类型的mParentWindow变量,这个变量在attach时传入当前Activity的Window。而Activity的Window里面的mAppToken是当前Activity的token。
所以最终是因为不能为Dialog提供正确的token
PopupWindow的子窗口添加流程是什么样的?
WMS为PopupWindow窗口创建一个子窗口分组WindowToken,每个子窗口都会有一个指向父窗口的引用,因为是利用父窗口IWindow作为键值,父窗口可以方便利用IWindow获取WindowToken,存入map中,进而得到全部的子窗口。
窗口的Z次序管理是什么样的?
在WMS中,窗口被抽象成WindowState,采用了三个int值来标志窗口所在位置。前两个主要根据窗口类型确定窗口位置,mLayer才是真正的值。
1 | final class WindowState implements WindowManagerPolicy.WindowState { |
由于前两个都是final修饰的,所以只有最后一个值可以改。
从坐标系知道,值越大,窗口越靠上。
对于Activity等应用窗口来说,主序都是一样的,怎么定他们真正的Z-order呢?其实是Activity的顺序由AMS来保证的。
在系统层面,决定了不同类型窗口所处的位置,比如系统Toast类型的窗口一定处于所有应用窗口之上
添加窗口的示意代码如下
1 | addWindow(){ |
Z-order通过addWindowToListInOrderLocked及assignLayersLocked才能确定
第一步,new一个windowstate实例。
第二步,addWindowToListInOrderLocked主要根据窗口的Token找到归属,插入到对应Token的WindowState列表,插入到特定位置后,Z-order就确定了。
第三步,通过assignLayersLocked为WindowState分配真正的Z-order mLayer。
当mLayer(int)确定后,这个顺序最终确定,之后,在SurfaceFlinger图层混排的时候处理。
WMS中窗口次序分配如何影响SurfaceFlinger服务?
SurfaceFlinger图层混排的时候不会混排所有的窗口,只会混排可见的窗口,比如有多个全屏Activity的时候,SurfaceFlinger只会处理最上面的。
1 | SurfaceControl.openTransaction(); |
通过事务来确保完整性,包括surface次序调整,通知SurfaceFlinger更新Surface信息。